////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Crytek Character Animation source code
//	
//	History:
//	10/9/2004 - Created by Ivo Herzeg <ivo@crytek.de>
//
//  Contains:
//  default-modelskeleton of the model   
/////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef _CRY_MODELSKELETON
#define _CRY_MODELSKELETON

#include "Skeleton.h"

#include "ModelJoint.h"      //embedded

#define NODEINDEX_NONE 255
#define NODEINDEX_TYPE uint8
#define NO_ENTRY_FOUND -1

template <class T, int BUCKET_AMOUNT>
class CInt32HashMap
{
private:
	inline uint32 HashFunction(uint32 key) const 
	{ 
		return (key * 1664525 + 1013904223) % BUCKET_AMOUNT;
	};

	struct Node
	{
		T value;
		int32 key;
		NODEINDEX_TYPE next;
		Node(): next(NODEINDEX_NONE) {};
	};

	struct Bucket
	{
		NODEINDEX_TYPE firstNode;
		Bucket(): firstNode(NODEINDEX_NONE) {};
	};

	Bucket m_buckets[BUCKET_AMOUNT];
	Node* m_nodes;
	NODEINDEX_TYPE m_nextFreeNode;
	int16 m_nodeAmount;
public:
	CInt32HashMap(int16 nodeAmount) 
	{
		m_nodes = new Node[nodeAmount];
		m_nodeAmount = nodeAmount;
		m_nextFreeNode = 0;
	}
	~CInt32HashMap()
	{
		delete[] m_nodes;
	}

	void Clear()
	{
		for (int i = 0; i < BUCKET_AMOUNT; ++i)
		{
			m_buckets[i].firstNode = NODEINDEX_NONE;
		}
		for (int i = 0; i < m_nodeAmount; ++i)
		{
			m_nodes[i].next = NODEINDEX_NONE;
		}
		m_nextFreeNode = 0;
	}

	void Insert(uint32 key, T value)
	{
		uint32 index = HashFunction(key);
		assert(value != NODEINDEX_NONE);
		assert(m_nextFreeNode < m_nodeAmount);
		assert(index < BUCKET_AMOUNT);
		if (m_buckets[index].firstNode == NODEINDEX_NONE)
		{
			m_nodes[m_nextFreeNode].value = value;
			m_nodes[m_nextFreeNode].key = key;
			m_buckets[index].firstNode = m_nextFreeNode;
			m_nextFreeNode++;
			return;
		}
		else
		{
			NODEINDEX_TYPE node = m_buckets[index].firstNode;
			while(1)
			{
				if (m_nodes[node].next == NODEINDEX_NONE)
				{
					m_nodes[m_nextFreeNode].value = value;
					m_nodes[m_nextFreeNode].key = key;
					m_nodes[node].next = m_nextFreeNode;
					m_nextFreeNode++;
					return;
				}
				node = m_nodes[node].next;
			}
		}
	}

	int32 Retrieve(uint32 key) const
	{
		uint32 index = HashFunction(key);
		NODEINDEX_TYPE node = m_buckets[index].firstNode;
		while(node != NODEINDEX_NONE)
		{
			if (m_nodes[node].key == key)
			{
				return m_nodes[node].value;
			}
			node = m_nodes[node].next;
		}
		return NO_ENTRY_FOUND;
	}

	void GetMemoryUsage(ICrySizer *pSizer) const
	{
		pSizer->AddObject(m_buckets, sizeof(Bucket) * BUCKET_AMOUNT);
		pSizer->AddObject(m_nodes, sizeof(Node) * m_nodeAmount);
		pSizer->AddObject(m_nextFreeNode);
		pSizer->AddObject(m_nodeAmount);
	}
};

struct IdxAndName
{
	int32 m_idxJoint; 
	const char* m_strJoint; 
	IdxAndName()
	{
		m_idxJoint =-1;;
		m_strJoint = 0;
	}

	void GetMemoryUsage( ICrySizer *pSizer ) const
	{
		pSizer->AddObject( m_strJoint );
	}
};
struct IKLimbType
{
	uint64 m_nHandle;  //64-bit string 
	uint32 m_nSolver;  //32-bit string 
	uint32 m_nInterations;  //only need for iterative solvers 
	f32    m_fThreshold;    //only need for iterative solvers 
	f32    m_fStepSize;     //only need for iterative solvers 
	DynArray<IdxAndName> m_arrJointChain;
	DynArray<int16>	m_arrLimbChildren;
	DynArray<int16>	m_arrRootToEndEffector;
	IKLimbType()
	{
		m_nHandle		= 0;
		m_nSolver		= 0;
	}

	void GetMemoryUsage( ICrySizer *pSizer ) const
	{
		pSizer->AddObject( m_arrJointChain );
		pSizer->AddObject( m_arrLimbChildren );
		pSizer->AddObject( m_arrRootToEndEffector );
	}
};

struct SRecoilJoints
{
	const char* m_strJointName;
	int16 m_nIdx;
	int16 m_nArm;
	f32   m_fWeight;
	f32   m_fDelay;
	SRecoilJoints() 
	{
		m_strJointName=0;
		m_nIdx=-1;
		m_nArm=0;
		m_fWeight=0.0f;
		m_fDelay=0.0f;
	};
	void GetMemoryUsage(ICrySizer *pSizer ) const {}
};

struct ProcAdjust
{
	const char* m_strJointName;
	int16 m_nIdx;
	ProcAdjust() 
	{
		m_strJointName=0;
		m_nIdx=-1;
	};
	void GetMemoryUsage(ICrySizer *pSizer ) const {}
};

struct ADIKTarget
{
	uint64 m_nHandle; 
	int32 m_idxTarget; 
	const char* m_strTarget; 
	int32 m_idxWeight;
	const char* m_strWeight;
	ADIKTarget()
	{
		m_nHandle		= 0;
		m_idxTarget	= -1; 
		m_strTarget	= 0; 
		m_idxWeight	= -1;
		m_strWeight	= 0;
	}
	void GetMemoryUsage(ICrySizer *pSizer ) const {}
};

//////////////////////////////////////////////////////////////////////////


class CModelSkeleton :
	public ICharacterModelSkeleton,
	public _reference_target_t
{
	static const int ELastValue = eIM_Last;

public:
	CModelSkeleton()
	{
		memset(m_IdxArray, -1, sizeof(m_IdxArray));
		m_useLookIK=0;
		m_fLookFieldOfView=180.0f;
		m_RecoilRWeaponBoneIdx=-1;
		m_RecoilLWeaponBoneIdx=-1;
		m_pJointsCRCToIDMap = NULL;
		m_bHasPhysicsProxies = false;
		m_charIKTargetData = new SIKTargetCharData();
	}

	~CModelSkeleton()
	{
		SAFE_DELETE(m_charIKTargetData);
		if(m_pJointsCRCToIDMap)
			delete m_pJointsCRCToIDMap;
	}

	const CModelJoint* GetJoints() const { return &m_arrModelJoints[0]; }
	uint32 GetJointCount() const { return m_arrModelJoints.size(); };

	const Skeleton::CPoseData& GetPoseData() const { return m_poseData; }

	void SetIKJointID();

	// Obsolete.
	uint32 GetNumJoints() const { return m_arrModelJoints.size(); };	
	int32 GetJointIDByName(const char* strJointName) const;
	int32 GetJointIDByCRC32(uint32 crc32) const;
	const char* GetJointNameByID(int32 nJointID) const;
	
	int32 GetJointParentIDByID(int32 nChildID) const
	{
		int32 numJoints = m_arrModelJoints.size();
		if(nChildID>=0 && nChildID<numJoints)
			return m_arrModelJoints[nChildID].m_idxParent;
		assert(0);
		return -1;
	}

	int32 GetJointIDByType(EAnimationJointType eType) const { return m_IdxArray[eType]; }

	int32 GetLimbDefinitionIdx(uint64 nHandle) const
	{
		uint32 numLimbTypes = m_IKLimbTypes.size();
		for (uint32 lt=0; lt<numLimbTypes; lt++)
		{
			if (nHandle==m_IKLimbTypes[lt].m_nHandle)
				return lt;
		}
		return -1;
	}
	const IKLimbType* GetLimbDefinition(int32 index) const { return &m_IKLimbTypes[index]; }

	int32 GetLimbEndeffectorIdx(uint64 handle) const { return m_IKLimbTypes[GetLimbDefinitionIdx(handle)].m_arrRootToEndEffector.back(); }
	int32 GetLimbRootIdx(uint64 handle) const 
	{ 
		return m_IKLimbTypes[GetLimbDefinitionIdx(handle)].m_arrJointChain.at(1).m_idxJoint; 
	}

	const SIKTargetCharData* GetCharIKTargetData() const;

	CModelJoint* GetParent (uint32 i) 
	{
		int32 pidx = m_arrModelJoints[i].m_idxParent;
		if (pidx>=0)
			return &m_arrModelJoints[pidx];
		else
			return 0;
	}

	// return bone index from the index map
	inline uint16 GetIdx(EAnimationJointType eType)
	{
		return m_IdxArray[eType];
	}

	void PrepareJointIDHash()
	{
		int numBones = m_arrModelJoints.size();

		if (m_pJointsCRCToIDMap)
			delete m_pJointsCRCToIDMap;
		m_pJointsCRCToIDMap = new CInt32HashMap<uint8,512>(numBones);

		for (int i = 0; i < numBones; ++i)
		{
			uint32 crc32Lower = m_arrModelJoints[i].m_nJointCRC32Lower;
			m_pJointsCRCToIDMap->Insert(crc32Lower,i);
		}
	}

	uint32 SizeOfSkeleton();
	void GetMemoryUsage( ICrySizer *pSizer ) const;

	// This is the bone hierarchy. All the bones of the hierarchy are present in this array
	DynArray<CModelJoint> m_arrModelJoints;
	// this dramatically accelerates access to JointIDs by CRC - overall consumption should be less than
	// 100 kb throughout the game (2-3 kb per ModelSkeleton)
	CInt32HashMap<uint8, 512>* m_pJointsCRCToIDMap;
	Skeleton::CPoseData m_poseData;

	int16	m_IdxArray[ELastValue]; //Index list with important joints. Needed for IK

	//array with limbs we can apply our 2-bone,3-bone or CCD-IK solvers
	DynArray<IKLimbType>				m_IKLimbTypes;					
	
	//array with Animation Driven IK-Targets
	DynArray<ADIKTarget>				m_ADIKTargets;					

	//mirror-joints for this model
	//DynArray<int16>							m_arrMirrorJoints;


	//these are the IK-Handles to lock the feet to the ground in case of upper body animation that modify the pelvis
	string m_strFeetLockLIKHandle;
	string m_strFeetLockRIKHandle;

	//pose-modifier values for recoil
	DynArray<SRecoilJoints>	m_RecoilJoints;
	string m_strRecoilLIKHandle;
	string m_strRecoilRIKHandle;
	int32 m_RecoilRWeaponBoneIdx;
	int32 m_RecoilLWeaponBoneIdx;


	uint32	m_useLookIK;						//rotational joints used for Aim-IK
	f32	    m_fLookFieldOfView;			//
	string 	m_strEyeAttachL;				//left eyeball attachment
	string 	m_strEyeAttachR;				//right eyeball attachment
	DynArray<DirectionalBlends> m_LookDirBlends;			//parameters for aiming
	DynArray<SJointsAimIK_Rot>	m_LookIK_Rot;					//rotational joints used for Look-IK
	DynArray<SJointsAimIK_Pos>	m_LookIK_Pos;					//positional joints used for Look-IK

	//pose-modifier values for Aim-IK and Look-IK
	DynArray<DirectionalBlends> m_AimDirBlends;				//parameters for aiming
	DynArray<SJointsAimIK_Rot>	m_AimIK_Rot;					//rotational joints used for Aim-IK
	DynArray<SJointsAimIK_Pos>	m_AimIK_Pos;					//positional joints used for Aim-IK
	DynArray<ProcAdjust>				m_AimProcAdjustments;	//array of spine joints or Aim-IK

	bool m_bHasPhysicsProxies; // tells if we are using CModelJoint::m_physInfo[1], for dead bodies
	SIKTargetCharData*			m_charIKTargetData;		// IK Target additional/optional data (from IK file) (for IKTarget and Object Interaction System)

}_ALIGN(128);



#endif
